package org.jugile.util;

/*

Copyright (C) 2007-2011 Jukka Rahkonen  email: jukka.rahkonen@iki.fi

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PushbackReader;
import java.io.StringReader;
import java.lang.reflect.Array;

/**
 * ""
 * 
 * @author jukka.rahkonen@iki.fi
 */
public class PullReader extends Jugile {

	// currently pushback reader implementation 
	// TODO: check performance
	
	private PushbackReader rdr;
	private final static int BUFSIZE = 64;
	private char[] buf = new char[BUFSIZE];
	
	public PullReader(String data) {
		StringReader r = new StringReader(data);
		rdr = new PushbackReader(r, BUFSIZE);
	}
	
	public PullReader(InputStream is) {
		try {
			InputStreamReader ir = new InputStreamReader(is,"UTF-8");
			rdr = new PushbackReader(ir, BUFSIZE);
		} catch(Exception e) { fail(e); }
	}
	public PullReader(File f) {
		try {
			InputStreamReader ir = new InputStreamReader(new FileInputStream(f), "UTF-8");
			rdr = new PushbackReader(ir,BUFSIZE);
		} catch(Exception e) { fail(e); }
	}
	public PullReader(InputStreamReader ir) {
		rdr = new PushbackReader(ir,BUFSIZE);
	}
	
	public char next() { return (char)readnext(); }	
	private int readnext() {
		try { 
			int ch = rdr.read();
			if (ch < 0) return ch;
			if ((char)ch == '\n') { line++; pos = 0;}
			else pos++;
			return ch;
		} catch(Exception e) { fail(e); return -1;}
	}
	
	private void unread(char ch) {
		try {
			if (ch == '\n') { line--; pos = 0;} // pos ?
			else pos--;
			rdr.unread(ch);
		} catch(Exception e) { fail(e); }
	}
	
	public boolean hasNext() {
		try {
			int ch = rdr.read();
			if (ch == -1) return false;
			rdr.unread(ch);
			return true;
		} catch (Exception e) { fail(e); return false; }
	}
	
	public char peek() {
		try { return peek(1); } 
		catch (Exception e) { fail(e); return (char)0; }
	}
	
	public char peek(int idx) {
		if (idx > BUFSIZE-1) fail("bufsize exeeded: "+idx);
		if (idx < 1) fail("peek idx starts from 1");
		try {
			int i = rdr.read(buf, 0, idx);
			if (i == -1) return (char)0;
			rdr.unread(buf, 0, idx);
			return buf[idx-1];
		} catch (Exception e) { fail(e); return (char)0; }
	}
	
	public void skip(int idx) {
		try { for (int i = 0; i < idx; i++) if (readnext() < 0) return;
		} catch (Exception e) { fail(e); }
	}
	
	public String str(int len) {
		if (len > BUFSIZE-1) fail("bufsize exeeded: "+len);
		if (len < 1) fail("str len must be at least 1");
		try {
			for (int i = 0; i < len; i++) {
				int ch = readnext();
				if (ch < 0) return String.copyValueOf(buf,0,i);
				buf[i] = (char)ch;
			}
			return String.copyValueOf(buf, 0, len);
		} catch (Exception e) { fail(e); return null; }		
	}
	
	public boolean match(String str) {
		String read = str(str.length());
		if (eq(str, read)) return true;
		// unread chars
		for (int i = read.length(); i > 0; i--) {
			push(read.charAt(i-1));
		}
		return false;
	}
	
	public String readUntil(String stopchars) { return readUntil(stopchars,null); }
	public String readUntil(String stopchars, String ignore) {
		Buffer buf = new Buffer();
		while (hasNext()) {
			char ch = next();
			//print("readuntil ch: '" + ch + "'");
			if (stopchars.indexOf(ch) >= 0) {
				push(ch);
				break;
			}
			if (!empty(ignore)) if (ignore.indexOf(ch) >= 0) continue;
			buf.add(ch);
		}
		return buf.toString();
	}
	
	public String readUntilStr(String end) {
		Buffer buf = new Buffer();
		while (hasNext()) {
			char ch = next();
			if (end.indexOf(ch) == 0) {
				if (end.length() == 1) return buf.toString();
				if (match(end.substring(1))) return buf.toString();
			}
			buf.add(ch);
		}
		fail("parse error. '"+end+"' not found"); return null;
	}
	
	public int trim() {
		int count = 0;
		while (hasNext()) {
			char ch = next();
			if (!Character.isWhitespace(ch)) { unread(ch); return count; }
			count++;
		}
		return count;
	}
	
	public String readIdentifier() {
		Buffer buf = new Buffer();
		while (hasNext()) {
			char ch = next();
			if (!isIdentifier(ch)) {
				push(ch);
				break;
			}
			buf.add(ch);
		}
		return buf.toString();
	}
	
	private boolean isIdentifier(char ch) {
		if (Character.isJavaIdentifierPart(ch)) return true;
		return false;
	}
	
	public void push(char ch) { unread(ch); }
	
	public void close() {
		try { if (rdr != null) rdr.close();} 
		catch (Exception e) { fail(e); }
	}
	
	private int line = 1;
	private int pos = 0;
	
	public String getPositionString() {
		return "position line: "+ line + " pos: " + pos;
	}
	public int getLine() { return line; }
	public int getPos() { return pos; }
	
}
